package com.starsky.websocket.handler;

import com.alibaba.fastjson.JSONObject;
import com.starsky.common.utils.DateUtils;
import com.starsky.websocket.domain.Result;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 文本消息处理
 */
public class TextWebSocketHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    private Logger log = LoggerFactory.getLogger(this.getClass()); // 日志对象
    //连接信息，连接通道id->channel
    public static Map<String, ChannelHandlerContext> queue = new ConcurrentHashMap<>();
    //用户连接信息，道路id->连接通道channel
    public static Map<String, String> section_queue = new ConcurrentHashMap<>();
    //连接地址后缀
    public static final String DEFAULT_WEB_SOCKET_LINK = "/ws";

    /**
     * 经过测试，在 ws 的 uri 后面不能传递参数，不然在 netty 实现 websocket 协议握手的时候会出现断开连接的情况。
     * 针对这种情况在 websocketHandler 之前做了一层 地址过滤，然后重写request 的 uri，并传入下一个管道中，基本上解决了这个问题。
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (null != msg && msg instanceof FullHttpRequest) {
            FullHttpRequest request = (FullHttpRequest) msg;
            log.info("调用 channelRead request.uri() [ {} ]", request.uri());
            log.info("Origin [ {} ] [ {} ]", request.headers().get("Origin"), request.headers().get("Host"));

            String uri = request.uri();
            String origin = request.headers().get("Origin");
            if (null == origin) {
                log.info("origin 为空 ");
                ctx.close();
            } else if (null != uri
                    && uri.contains(DEFAULT_WEB_SOCKET_LINK)
                    && uri.contains("?")) {
                String[] uriArray = uri.split("\\?");
                if (null != uriArray && uriArray.length > 1) {
                    String[] paramsArray = uriArray[1].split("=");
                    if (null != paramsArray && paramsArray.length > 1) {
                        String roadSectionId = paramsArray[1];
                        section_queue.put(ctx.channel().id().toString(), roadSectionId);
                        log.info("ws：建立连接ID:" + ctx.channel().id().toString() + " ,roadSecionId: " + roadSectionId);
                    }
                }
                request.setUri(DEFAULT_WEB_SOCKET_LINK);
            } else {
                log.info("WS请求url未携带参数：", origin);
//                log.info("不允许 [ {} ] 连接 强制断开", origin);
//                ctx.close();
            }
        }
        super.channelRead(ctx, msg);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) {
        log.info("接收到客户端的消息:[{}]", msg.text());
        // 如果是向客户端发送文本消息，则需要发送 TextWebSocketFrame 消息
        InetSocketAddress inetSocketAddress = (InetSocketAddress) ctx.channel().remoteAddress();
        String ip = inetSocketAddress.getHostName();
        String txtMsg = "[" + ip + "][" + DateUtils.format(new Date(), DateUtils.YMDHMSS) + "] ==> " + msg.text();
        //TODO 这是发给自己
//        ctx.channel().writeAndFlush(new TextWebSocketFrame(txtMsg));
        ctx.writeAndFlush(new TextWebSocketFrame(JSONObject.toJSONString(new Result<String>(200, "连接成功", txtMsg))));
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        log.error("服务器发生异常: [ {} ]", cause);
        //移除map
        queue.remove(ctx.channel().id().toString());
        section_queue.remove(ctx.channel().id().toString(), ctx);
        ctx.close();
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.info("与客户端建立连接，通道开启...");
        log.info(" 客户端加入 [ {} ]", ctx.channel().id().toString());
        // 添加
        queue.put(ctx.channel().id().toString(), ctx);
        super.channelActive(ctx);
    }

    /**
     * 离线
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.info("与客户端断开连接，通道关闭...");
        log.info(" 离线 [ {} ] ", ctx.channel().id().asLongText());

        // 移除
        queue.remove(ctx.channel().id().toString());
        section_queue.remove(ctx.channel().id().toString(), ctx);
        super.channelInactive(ctx);
        ctx.close();
    }
}
